/*  PCSX2 - PS2 Emulator for PCs
 *  Copyright (C) 2002-2010  PCSX2 Dev Team
 *
 *  PCSX2 is free software: you can redistribute it and/or modify it under the terms
 *  of the GNU Lesser General Public License as published by the Free Software Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE.  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with PCSX2.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

wxDECLARE_EVENT(pxEvt_StartIdleEventTimer, wxCommandEvent);
wxDECLARE_EVENT(pxEvt_DeleteObject, wxCommandEvent);
wxDECLARE_EVENT(pxEvt_DeleteThread, wxCommandEvent);

typedef void FnType_Void();

// --------------------------------------------------------------------------------------
//  SynchronousActionState
// --------------------------------------------------------------------------------------
class SynchronousActionState
{
    DeclareNoncopyableObject(SynchronousActionState);

protected:
    bool m_posted;
    Threading::Semaphore m_sema;
    ScopedExcept m_exception;

public:
    sptr return_value;

    SynchronousActionState()
    {
        m_posted = false;
        return_value = 0;
    }

    virtual ~SynchronousActionState() = default;

    void SetException(const BaseException &ex);
    void SetException(BaseException *ex);

    Threading::Semaphore &GetSemaphore() { return m_sema; }
    const Threading::Semaphore &GetSemaphore() const { return m_sema; }

    void RethrowException() const;
    int WaitForResult();
    int WaitForResult_NoExceptions();
    void PostResult(int res);
    void ClearResult();
    void PostResult();
};


// --------------------------------------------------------------------------------------
//  pxSimpleEvent  -  non-abstract implementation of wxEvent
// --------------------------------------------------------------------------------------
// Why?  I mean, why is wxEvent abstract?  Over-designed OO is such a bad habit.  And while
// I'm on my high horse (and it's so very high!), why use 'private'?  Ever?  Seriously, it
// sucks.  Stop using it, people.
//
class pxSimpleEvent : public wxEvent
{
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxSimpleEvent)

public:
    explicit pxSimpleEvent(int evtid = 0)
        : wxEvent(0, evtid)
    {
    }

    pxSimpleEvent(wxWindowID winId, int evtid)
        : wxEvent(winId, evtid)
    {
    }

    virtual wxEvent *Clone() const { return new pxSimpleEvent(*this); }
};

// --------------------------------------------------------------------------------------
//  pxActionEvent
// --------------------------------------------------------------------------------------
class pxActionEvent;

wxDECLARE_EVENT(pxEvt_InvokeAction, pxActionEvent);

class pxActionEvent : public wxEvent
{
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxActionEvent)

protected:
    SynchronousActionState *m_state;

public:
    virtual ~pxActionEvent() = default;
    virtual pxActionEvent *Clone() const { return new pxActionEvent(*this); }

    explicit pxActionEvent(SynchronousActionState *sema = NULL, int msgtype = pxEvt_InvokeAction);
    explicit pxActionEvent(SynchronousActionState &sema, int msgtype = pxEvt_InvokeAction);
    pxActionEvent(const pxActionEvent &src);

    Threading::Semaphore *GetSemaphore() const { return m_state ? &m_state->GetSemaphore() : NULL; }

    const SynchronousActionState *GetSyncState() const { return m_state; }
    SynchronousActionState *GetSyncState() { return m_state; }

    void SetSyncState(SynchronousActionState *obj) { m_state = obj; }
    void SetSyncState(SynchronousActionState &obj) { m_state = &obj; }

    virtual void SetException(BaseException *ex);
    void SetException(const BaseException &ex);

    virtual void _DoInvokeEvent();

protected:
    // Extending classes should implement this method to perfoem whatever action it is
    // the event is supposed to do. :)  Thread affinity is garaunteed to be the Main/UI
    // thread, and exceptions will be handled automatically.
    //
    // Exception note: exceptions are passed back to the thread that posted the event
    // to the queue, when possible.  If the calling thread is not blocking for a result
    // from this event, then the exception will be posted to the Main/UI thread instead.
    virtual void InvokeEvent() {}
};


// --------------------------------------------------------------------------------------
//  pxExceptionEvent
// --------------------------------------------------------------------------------------
class pxExceptionEvent : public pxActionEvent
{
    typedef pxActionEvent _parent;

protected:
    BaseException *m_except;

public:
    pxExceptionEvent(BaseException *ex = NULL)
    {
        m_except = ex;
    }

    pxExceptionEvent(const BaseException &ex);

    virtual ~pxExceptionEvent()
    {
    }

    virtual pxExceptionEvent *Clone() const { return new pxExceptionEvent(*this); }

protected:
    void InvokeEvent();
};

// --------------------------------------------------------------------------------------
//  pxSynchronousCommandEvent
// --------------------------------------------------------------------------------------

class pxSynchronousCommandEvent : public wxCommandEvent
{
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxSynchronousCommandEvent)

protected:
    SynchronousActionState *m_sync;
    wxEventType m_realEvent;

public:
    virtual ~pxSynchronousCommandEvent() = default;
    virtual pxSynchronousCommandEvent *Clone() const { return new pxSynchronousCommandEvent(*this); }

    pxSynchronousCommandEvent(SynchronousActionState *sema = NULL, wxEventType commandType = wxEVT_NULL, int winid = 0);
    pxSynchronousCommandEvent(SynchronousActionState &sema, wxEventType commandType = wxEVT_NULL, int winid = 0);

    pxSynchronousCommandEvent(SynchronousActionState *sema, const wxCommandEvent &evt);
    pxSynchronousCommandEvent(SynchronousActionState &sema, const wxCommandEvent &evt);

    pxSynchronousCommandEvent(const pxSynchronousCommandEvent &src);

    Threading::Semaphore *GetSemaphore() { return m_sync ? &m_sync->GetSemaphore() : NULL; }
    wxEventType GetRealEventType() const { return m_realEvent; }

    void SetException(BaseException *ex);
    void SetException(const BaseException &ex);
};

wxDECLARE_EVENT(pxEvt_SynchronousCommand, pxSynchronousCommandEvent);

// --------------------------------------------------------------------------------------
//  BaseMessageBoxEvent
// --------------------------------------------------------------------------------------
class BaseMessageBoxEvent : public pxActionEvent
{
    typedef pxActionEvent _parent;
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(BaseMessageBoxEvent)

protected:
    wxString m_Content;

public:
    virtual ~BaseMessageBoxEvent() = default;
    virtual BaseMessageBoxEvent *Clone() const { return new BaseMessageBoxEvent(*this); }

    explicit BaseMessageBoxEvent(const wxString &content = wxEmptyString, SynchronousActionState *instdata = NULL);
    BaseMessageBoxEvent(const wxString &content, SynchronousActionState &instdata);
    BaseMessageBoxEvent(const BaseMessageBoxEvent &event);

protected:
    virtual void InvokeEvent();
    virtual int _DoDialog() const;
};

// --------------------------------------------------------------------------------------
//  MsgButtons
// --------------------------------------------------------------------------------------
class MsgButtons
{
protected:
    BITFIELD32()
    bool
        m_OK : 1,
        m_Cancel : 1,
        m_Yes : 1,
        m_No : 1,
        m_AllowToAll : 1,
        m_Apply : 1,
        m_Abort : 1,
        m_Retry : 1,
        m_Ignore : 1,
        m_Reset : 1,
        m_Close : 1;
    BITFIELD_END

    wxString m_CustomLabel;
    wxString m_CustomLabelId;

public:
    MsgButtons() { bitset = 0; }

    MsgButtons &OK()
    {
        m_OK = true;
        return *this;
    }
    MsgButtons &Cancel()
    {
        m_Cancel = true;
        return *this;
    }
    MsgButtons &Apply()
    {
        m_Apply = true;
        return *this;
    }
    MsgButtons &Yes()
    {
        m_Yes = true;
        return *this;
    }
    MsgButtons &No()
    {
        m_No = true;
        return *this;
    }
    MsgButtons &ToAll()
    {
        m_AllowToAll = true;
        return *this;
    }

    MsgButtons &Abort()
    {
        m_Abort = true;
        return *this;
    }
    MsgButtons &Retry()
    {
        m_Retry = true;
        return *this;
    }
    MsgButtons &Ignore()
    {
        m_Ignore = true;
        return *this;
    }
    MsgButtons &Reset()
    {
        m_Reset = true;
        return *this;
    }
    MsgButtons &Close()
    {
        m_Close = true;
        return *this;
    }

    // label - native language label displayed to user
    // id - raw ASCII identifier used in the config file (do not translate, hence char*)
    MsgButtons &Custom(const wxString &label, const char *id)
    {
        m_CustomLabel = label;
        m_CustomLabelId = fromUTF8(id);
        return *this;
    }

    MsgButtons &OKCancel()
    {
        m_OK = m_Cancel = true;
        return *this;
    }
    MsgButtons &YesNo()
    {
        m_Yes = m_No = true;
        return *this;
    }

    bool HasOK() const { return m_OK; }
    bool HasCancel() const { return m_Cancel; }
    bool HasApply() const { return m_Apply; }
    bool HasYes() const { return m_Yes; }
    bool HasNo() const { return m_No; }
    bool AllowsToAll() const { return m_AllowToAll; }

    bool HasAbort() const { return m_Abort; }
    bool HasRetry() const { return m_Retry; }
    bool HasIgnore() const { return m_Ignore; }
    bool HasReset() const { return m_Reset; }
    bool HasClose() const { return m_Close; }

    bool HasCustom() const { return !m_CustomLabel.IsEmpty(); }
    const wxString &GetCustomLabel() const { return m_CustomLabel; }
    const wxString &GetCustomLabelId() const { return m_CustomLabelId; }

    bool Allows(wxWindowID id) const;
    void SetBestFocus(wxWindow *dialog) const;
    void SetBestFocus(wxWindow &dialog) const;

    bool operator==(const MsgButtons &right) const
    {
        return OpEqu(bitset);
    }

    bool operator!=(const MsgButtons &right) const
    {
        return !OpEqu(bitset);
    }
};

// --------------------------------------------------------------------------------------
//  pxMessageBoxEvent
// --------------------------------------------------------------------------------------
// This event type is used to transfer message boxes to the main UI thread, and return the
// result of the box.  It's the only way a message box can be issued from non-main threads
// with complete safety in wx2.8.
//
// For simplicity sake this message box only supports two basic designs.  The main design
// is a generic message box with confirmation buttons of your choosing.  Additionally you
// can specify a "scrollableContent" text string, which is added into a read-only richtext
// control similar to the console logs and such.
//
// Future consideration: If wxWidgets 3.0 has improved thread safety, then it should probably
// be reasonable for it to work with a more flexable model where the dialog can be created
// on a child thread, passed to the main thread, where ShowModal() is run (keeping the nested
// message pumps on the main thread where they belong).  But so far this is not possible,
// because of various subtle issues in wx2.8 design.
//
class pxMessageBoxEvent : public BaseMessageBoxEvent
{
    typedef BaseMessageBoxEvent _parent;
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxMessageBoxEvent)

protected:
    wxString m_Title;
    MsgButtons m_Buttons;

public:
    virtual ~pxMessageBoxEvent() = default;
    virtual pxMessageBoxEvent *Clone() const { return new pxMessageBoxEvent(*this); }

    pxMessageBoxEvent() {}
    pxMessageBoxEvent(const wxString &title, const wxString &content, const MsgButtons &buttons, SynchronousActionState &instdata);
    pxMessageBoxEvent(const wxString &title, const wxString &content, const MsgButtons &buttons, SynchronousActionState *instdata = NULL);
    pxMessageBoxEvent(const pxMessageBoxEvent &event) = default;

protected:
    int _DoDialog() const;
};

// --------------------------------------------------------------------------------------
//  pxAssertionEvent
// --------------------------------------------------------------------------------------
class pxAssertionEvent : public BaseMessageBoxEvent
{
    typedef BaseMessageBoxEvent _parent;
    DECLARE_DYNAMIC_CLASS_NO_ASSIGN(pxAssertionEvent)

protected:
    wxString m_Stacktrace;

public:
    virtual ~pxAssertionEvent() = default;
    virtual pxAssertionEvent *Clone() const { return new pxAssertionEvent(*this); }

    pxAssertionEvent(const wxString &content = wxEmptyString, const wxString &trace = wxEmptyString, SynchronousActionState *instdata = NULL);
    pxAssertionEvent(const wxString &content, const wxString &trace, SynchronousActionState &instdata);
    pxAssertionEvent(const pxAssertionEvent &event) = default;

    pxAssertionEvent &SetStacktrace(const wxString &trace);

protected:
    int _DoDialog() const;
};
